package com.hsgene.authority.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hsgene.authority.domain.GeneticTestingOperationAccountPermission;
import com.hsgene.authority.domain.GeneticTestingOperationPermission;
import com.hsgene.authority.dto.OperationPermissionTree;
import com.hsgene.authority.exception.AuthorityErrorCode;
import com.hsgene.authority.persistence.PermissionRepository;
import com.hsgene.authority.service.OperationAccountPermissionService;
import com.hsgene.authority.service.PermissionService;
import com.hsgene.common.DeleteFlagType;
import com.hsgene.common.util.basic.JsonUtils;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.common.util.validation.FieldChecker;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 权限服务ServiceImpl
 *
 * @author wangbing
 * @version 1.0, 2018/11/5
 */
@Service
public class PermissionServiceImpl implements PermissionService {

    private final static Logger LOGGER = LoggerFactory.getLogger(PermissionServiceImpl.class);

    @Resource
    private PermissionRepository permissionRepository;

    @Resource
    private RedisTemplate<Serializable, Serializable> redisTemplate;

    @Resource
    private OperationAccountPermissionService operationAccountPermissionService;

    @Value("${redis.prefix.permission.trees}")
    private String permissionTreesRedisPrefix;

    @PostConstruct
    public void initPermissionTrees() {
        List<GeneticTestingOperationPermission> permissions = permissionRepository.getAllAvailablePermission();
        if (CollectionUtils.isEmpty(permissions)) {
            LOGGER.warn("尚未初始化权限！");
            return;
        }
        List<OperationPermissionTree> trees = Lists.newArrayList();
        Map<String, OperationPermissionTree> permissionMap = Maps.newHashMap();
        OperationPermissionTree root = new OperationPermissionTree();
        root.setId("root");
        root.setPermissionName("全部");
        root.setLevel(-1);
        root.setPosition(-1);
        root.setStatus(1);
        root.setDeleteFlag(0);
        root.setSelected(true);
        root.setChildrenPermissions(Lists.newArrayList());
        permissionMap.put(root.getId(), root);
        Map<String, List<OperationPermissionTree>> childrenPermissionMap = Maps.newHashMap();
        permissions.forEach(e -> {
            OperationPermissionTree tree = new OperationPermissionTree();
            BeanUtils.copyProperties(e, tree);
            tree.setChildrenPermissions(Lists.newArrayList());
            if (tree.getCommonFlag() == 1) {
                tree.setSelected(true);
            } else {
                tree.setSelected(false);
            }
            if (StringUtils.isBlank(tree.getParentId())) {
                tree.setParentId("root");
            }

            trees.add(tree);
            permissionMap.put(tree.getId(), tree);
        });
        trees.forEach(e -> {
            String parentId = e.getParentId() == null ? "root" : e.getParentId();
            List<OperationPermissionTree> children = childrenPermissionMap.get(parentId);
            if (CollectionUtils.isEmpty(children)) {
                children = Lists.newArrayList();
                childrenPermissionMap.put(parentId, children);
            }
            children.add(e);
        });
        childrenPermissionMap.forEach((parentId, children) -> {
            permissionMap.get(parentId).setChildrenPermissions(children);
        });

        redisTemplate.opsForValue().set(permissionTreesRedisPrefix, JsonUtils.objectToJsonIgnoreNull(permissionMap.get("root")));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<NullObject> addPermission(GeneticTestingOperationPermission permission) {
        FieldChecker.checkEmpty(permission, "权限信息");
        permission.check();

        // 查询父级节点
        String parentId = permission.getParentId();
        if (StringUtils.isBlank(parentId) || StringUtils.equals("root", parentId)) {
            parentId = null;
            permission.setParentId(null);
            permission.setLevel(1);
        } else {
            GeneticTestingOperationPermission parent = permissionRepository.findById(parentId);
            if (parent == null || DeleteFlagType.DELETE.getCode() == parent.getDeleteFlag()) {
                return ApiResult.fail(AuthorityErrorCode.PARENT_PERMISSION_NOT_EXISTED);
            }
            if (parent.getCommonFlag() == 1) {
                return ApiResult.fail(AuthorityErrorCode.COMMON_PERMISSION_CANNOT_ADD_CHILD);
            }
            permission.setLevel(parent.getLevel() + 1);
        }
        // 查询当前位置节点是否重复
        /*GeneticTestingOperationPermission existed = permissionRepository.findByPosition(parentId, permission.getPosition());
        if (existed != null) {
            return ApiResult.fail(AuthorityErrorCode.POSITION_PERMISSION_DUPLICATE);
        }*/
        // 查询当前权限名称是否重复
        GeneticTestingOperationPermission existed = permissionRepository.findByChildName(parentId, permission.getPermissionName());
        if (existed != null) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_NAME_DUPLICATE);
        }
        permission.setId(UUID32.randomUUIDString());
        permission.setStatus(1);
        permission.setDeleteFlag(DeleteFlagType.NOT_DELETED.getCode());
        permission.setUpdateDateTime(new Date());
        permissionRepository.save(permission);

        initPermissionTrees();
        operationAccountPermissionService.refreshAllDepartmentPermission();

        return ApiResult.succ();
    }

    @Override
    public OperationPermissionTree getWholePermissionTrees() {
        Serializable serializable = redisTemplate.opsForValue().get(permissionTreesRedisPrefix);
        if (serializable == null) {
            initPermissionTrees();
            serializable = redisTemplate.opsForValue().get(permissionTreesRedisPrefix);
        }
        if (serializable == null) {
            return null;
        }

        return JsonUtils.jsonToObject((String) serializable, new TypeReference<OperationPermissionTree>() {
        });
    }

    @Override
    public OperationPermissionTree getPermissionTree(String permissionId, boolean wholeTree) {
        Serializable serializable = redisTemplate.opsForValue().get(permissionTreesRedisPrefix);
        if (serializable == null) {
            initPermissionTrees();
            serializable = redisTemplate.opsForValue().get(permissionTreesRedisPrefix);
        }
        if (serializable == null) {
            return null;
        }
        String json = (String) serializable;
        OperationPermissionTree tree = JsonUtils.jsonToObject(json, new TypeReference<OperationPermissionTree>() {
        });

        if (permissionId != null && !"root".equals(permissionId)) {
            tree = findPermissionTree(permissionId, tree.getChildrenPermissions());
        }
        if (tree != null && !wholeTree && !CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
            tree.getChildrenPermissions().forEach(e -> e.setChildrenPermissions(null));
        }
        return tree;
    }

    private OperationPermissionTree findPermissionTree(String permissionId, List<OperationPermissionTree> trees) {
        for (OperationPermissionTree tree : trees) {
            if (permissionId.equals(tree.getId())) {
                return tree;
            }
            if (!CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
                OperationPermissionTree leaf = findPermissionTree(permissionId, tree.getChildrenPermissions());
                if (leaf != null) {
                    return leaf;
                }
            }
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<NullObject> updatePermission(GeneticTestingOperationPermission permission) {
        FieldChecker.checkEmpty(permission, "权限信息");
        permission.checkForUpdate();

        GeneticTestingOperationPermission oldPermission = permissionRepository.findById(permission.getId());
        if (oldPermission == null || DeleteFlagType.DELETE.getCode() == oldPermission.getDeleteFlag()) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_NOT_EXISTED);
        }
        // 查询当前位置节点是否重复
        /*GeneticTestingOperationPermission existed = permissionRepository.findByPosition(oldPermission.getParentId(), permission.getPosition());
        if (existed != null && !existed.getId().equals(oldPermission.getId())) {
            return ApiResult.fail(AuthorityErrorCode.POSITION_PERMISSION_DUPLICATE);
        }*/
        // 查询当前权限名称是否重复
        GeneticTestingOperationPermission existed = permissionRepository.findByChildName(oldPermission.getParentId(), permission.getPermissionName());
        if (existed != null && !existed.getId().equals(oldPermission.getId())) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_NAME_DUPLICATE);
        }
        // 修改主权限为通用权限，需要校验是否存在子权限，存在则不允许修改
        if (oldPermission.getCommonFlag() == 0 && permission.getCommonFlag() == 1) {
            List<GeneticTestingOperationPermission> children = permissionRepository.findByParentId(oldPermission.getId());
            if (!CollectionUtils.isEmpty(children)) {
                return ApiResult.fail(AuthorityErrorCode.PERMISSION_CANNOT_TO_COMMON);
            }
        }
        permission.setStatus(oldPermission.getStatus());
        permission.setUpdateDateTime(new Date());

        permissionRepository.update(permission);
        initPermissionTrees();
        operationAccountPermissionService.refreshAllDepartmentPermission();

        return ApiResult.succ();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<NullObject> deletePermission(String permissionId) {
        FieldChecker.checkEmpty(permissionId, "权限id");

        OperationPermissionTree permissionTree = getPermissionTree(permissionId, true);
        if (permissionTree == null) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_NOT_EXISTED);
        }

        List<GeneticTestingOperationAccountPermission> permissionUsedList = permissionRepository.queryPermissionUsed(permissionId);
        if (!CollectionUtils.isEmpty(permissionUsedList)) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_CANNOT_DELETE);
        }
        deletePermissionByTree(permissionTree);
        initPermissionTrees();
        if (permissionTree.getCommonFlag() == 1) {
            operationAccountPermissionService.refreshAllDepartmentPermission();
        }

        return ApiResult.succ();
    }

    private void deletePermissionByTree(OperationPermissionTree tree) {
        if (tree != null) {
            permissionRepository.delete(tree.getId());
            if (!CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
                tree.getChildrenPermissions().forEach(this::deletePermissionByTree);
            }
        }
    }
}
